﻿using RATSharp.Property;
using RestSharp;
using System.Collections.Generic;
using System.Net;
using System.Web;
using RATSharp.Model;
using System.Xml.Serialization;
using RestSharp.Deserializers;
using System.Threading.Tasks;
using System;
namespace RATSharp.Rest
{
    delegate void somedelegat<T>(T user);

    /// <summary>
    /// Main Service for communication with server. Using REST services, 
    /// transformates every request execution into asynchronous task.
    /// 
    /// 
    /// </summary>
    public class RestService
    {
        /// <summary>
        /// identifier for authorization session.
        /// </summary>
        public static string SESSION_COOKIE_KEY = "JSESSIONID";
        /// <summary>
        /// Connection credentials;
        /// </summary>
        public Properties properties {get; set;}
        private RestClient restclient;
        /// <summary>
        /// session cookie from server.
        /// </summary>
        public RestResponseCookie sessionCookie{get; set;}

        /// <summary>
        /// Default contructor.
        /// </summary>
        /// <param name="properties">User login credentials.</param>
        public RestService(Properties properties)
        {
            this.properties = properties;
        }

        /// <summary>
        /// Initialise service;
        /// </summary>
        public void init()
        {
            //disable SSL certificate validation
            ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
            restclient = new RestClient(properties.url);
        }


        /// <summary>
        /// Adds new reservations into system.
        /// </summary>
        /// <param name="reservation">new Reservations</param>
        /// <returns>empty reservations in case request was succesfully finished. 
        /// Otwerwise returns reservation which is blocking our time periods.</returns>
        public Task<Reservations> addReservation(Reservations reservation)
        {
            var request = new RestRequest("/api/rest/users/session/reservation", Method.PUT);
            request.XmlSerializer.DateFormat = "yyyy-MM-dd-HH:mm";
            request.AddBody(reservation);
            return RestResponseTaskFactory.ExecuteTaskAsync<Reservations>(restclient, request, this);
        }

        /// <summary>
        /// Deletes reservations from server.
        /// </summary>
        /// <param name="reservation">reservation intended to delete.</param>
        /// <returns></returns>
        public Task<Reservations> deleteReservation(Reservation reservation)
        {
            var request = new RestRequest("/api/rest/users/session/reservation", Method.DELETE);
            request.AddParameter("id", reservation.id);
            return RestResponseTaskFactory.ExecuteTaskAsync<Reservations>(restclient, request, this);
        }


        /// <summary>
        /// Modifies period on server accordingly to local model.
        /// </summary>
        /// <param name="period">period to modify</param>
        /// <returns>modified period</returns>
        public Task<Period> modifyPeriod(Period period)
        {
            var request = new RestRequest("/api/rest/users/session/reservation/period", Method.POST);
            request.AddParameter("id", period.id);
            request.AddParameter("valid", period.valid);
            return RestResponseTaskFactory.ExecuteTaskAsync<Period>(restclient, request, this);
        }

        /// <summary>
        /// Try to pick up object.
        /// </summary>
        /// <param name="objId">object to pick up</param>
        /// <returns>empty collection if pick up was unsuccesful, alien reservation in case when someone else is occupying object now or picked reservation</returns>
        public Task<Reservations> pickUpObject(int objId)
        {
            var request = new RestRequest("/api/rest/objects/session/object/pickup", Method.POST);
            request.AddParameter("id", objId);
            return RestResponseTaskFactory.ExecuteTaskAsync<Reservations>(restclient, request, this);
        }

        /// <summary>
        /// Returns all reservations period which was sofar not returned
        /// </summary>
        /// <returns>all reservations period which was sofar not returned</returns>
        public Task<Reservations> getAllUnreturnedItems()
        {
            var request = new RestRequest("/api/rest/users/session/reservations/notreturned", Method.GET);
            return RestResponseTaskFactory.ExecuteTaskAsync<Reservations>(restclient, request, this);
        }

        /// <summary>
        /// Return item back to pool
        /// </summary>
        /// <param name="periodId">period identifier</param>
        /// <returns>revalidated period</returns>
        public Task<Reservations> returnItem(int? periodId)
        {
            var request = new RestRequest("/api/rest/users/session/reservations/return", Method.POST);
            request.AddParameter("id", periodId);
            return RestResponseTaskFactory.ExecuteTaskAsync<Reservations>(restclient, request, this);
        }

        /// <summary>
        /// Returns all reservations which owner is currently logged user.
        /// </summary>
        /// <param name="since">all reservations from date</param>
        /// <param name="to">all reservations to date</param>
        /// <returns>Returns all reservations which owner is currently logged user.</returns>
        public Task<Reservations> getReservationsByUserAsync(DateTime since, DateTime to)
        {
            String sinceAsString = String.Format("{0:yyyy-MM-dd-HH:mm}", since);
            String toAsString = String.Format("{0:yyyy-MM-dd-HH:mm}", to);


            var request = new RestRequest("/api/rest/users/session/reservations", Method.GET);
            request.DateFormat = "yyyy-MM-dd-HH:mm";
            request.AddParameter("since", sinceAsString);
            request.AddParameter("to", toAsString);
            return RestResponseTaskFactory.ExecuteTaskAsync<Reservations>(restclient, request, this);
        }

        /// <summary>
        /// Returns all reservations from specified object.
        /// </summary>
        /// <param name="since">all reservations from date</param>
        /// <param name="to">all reservations to date</param>
        /// <param name="id">object id</param>
        /// <returns>ll reservations from specified object.</returns>
        public Task<Reservations> getReservationsObject(DateTime since, DateTime to, int id)
        {
            String sinceAsString = String.Format("{0:yyyy-MM-dd-HH:mm}", since);
            String toAsString = String.Format("{0:yyyy-MM-dd-HH:mm}", to);


            var request = new RestRequest("/api/rest/objects/object/reservations", Method.GET);
            request.DateFormat = "yyyy-MM-dd-HH:mm";

            request.AddParameter("since", sinceAsString);
            request.AddParameter("to", toAsString);
            request.AddParameter("id", id);
            return RestResponseTaskFactory.ExecuteTaskAsync<Reservations>(restclient, request, this);
        }
  
        /// <summary>
        /// Returns  all favourites objects which owner is currently logged user.
        /// </summary>
        /// <returns>all favourites objects which owner is currently logged user</returns>
        public Task<Objects> getFavouritesAsync()
        {
            var request = new RestRequest("/api/rest/users/user/favourites", Method.GET);
            return RestResponseTaskFactory.ExecuteTaskAsync<Objects>(restclient, request, this);
        }

        /// <summary>
        /// Searches for object.
        /// </summary>
        /// <param name="key">part of requested search string</param>
        /// <param name="searchBy">Search by enum value</param>
        /// <returns>All searched objects.</returns>
        public Task<Objects> seachObjects(string key, SearchBy searchBy)
        {
            var request = new RestRequest("/api/rest/objects/object/search", Method.GET);
            request.AddParameter("key", key);
            request.AddParameter("type", searchBy);
            return RestResponseTaskFactory.ExecuteTaskAsync<Objects>(restclient, request, this);
        }

        /// <summary>
        /// Adds new favourite object to server.  
        /// </summary>
        /// <param name="objectId">favourites object id</param>
        /// <returns>updated favourite list</returns>
        public Task<Objects> addFavourite(int objectId)
        {
            var request = new RestRequest("/api/rest/users/session/favourite", Method.POST);
            request.AddParameter("id", objectId);
            restclient.Authenticator = new HttpBasicAuthenticator(properties.username, properties.password);
            IRestResponse response = restclient.Execute(request);

            return RestResponseTaskFactory.ExecuteTaskAsync<Objects>(restclient, request, this);
        }

        /// <summary>
        /// Remove favourite object to current users favourites list.
        /// </summary>
        /// <param name="objectId">object to remove</param>
        /// <returns>updated favourite list</returns>
        public Task<Objects> removeFavourite(int objectId)
        {
            var request = new RestRequest("/api/rest/users/session/favourite", Method.DELETE);
            request.AddParameter("objectid", objectId);
            return RestResponseTaskFactory.ExecuteTaskAsync<Objects>(restclient, request, this);
        }

        /// <summary>
        /// Get user by users name (use for logging and establishing communication)
        /// </summary>
        /// <param name="name">user name</param>
        /// <returns>logged user credentials</returns>
        public Task<User> getUserAsync(String name)
        {
            var request = new RestRequest("/api/rest/users/user/name", Method.GET);
            request.AddParameter("name", properties.username);
            return RestResponseTaskFactory.ExecuteTaskAsync<User>(restclient, request, this);
        }

    }
    /// <summary>
    /// Defines a "seach type" which shall be run on server side.
    /// 
    /// </summary>
       public enum SearchBy { 
           /// <summary>
           /// Search in all searchable columns.
           /// </summary>
           ALL = 0,
           /// <summary>
           /// Search only by object name.
           /// </summary>
           OBJ_NAME = 1,
           /// <summary>
           /// Search only by class name.
           /// </summary>
           CLASS_NAME = 2,
           /// <summary>
           /// Search only by object description.
           /// </summary>
           OBJ_DESCRIPTION = 3
       }
}